namespace BizTalk.ESB.Extensions.PipelineComponents
{
    using System;
    using System.IO;
    using System.Text;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Resources;
    using System.Reflection;
    using System.Diagnostics;
    using System.Collections;
    using System.ComponentModel;
    using Microsoft.BizTalk.Message.Interop;
    using Microsoft.BizTalk.Component.Interop;
    using Microsoft.BizTalk.Component;
    using Microsoft.BizTalk.Streaming;
    using Microsoft.Practices.ESB.Exception.Management;
    using Microsoft.Practices.ESB.Utilities;
    using System.Globalization;
    
    
    [ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [System.Runtime.InteropServices.Guid("7f19364e-e28a-4282-8661-5e14d1ef73dc")]
    [ComponentCategory(CategoryTypes.CATID_DisassemblingParser)]
    public class ImageDisassembler : Microsoft.BizTalk.Component.Interop.IDisassemblerComponent, Microsoft.BizTalk.Component.Interop.IProbeMessage, IBaseComponent, IPersistPropertyBag, IComponentUI
    {

        private const string DescriptionProperty = "Parses images files and splits if multi-image";
        private const string NameProperty = "BizTalk.ESB.Extensions.PipelineComponent.ImageDisassembler";
        private const string VersionProperty = "1.0";
        //private System.Resources.ResourceManager resourceManager = new System.Resources.ResourceManager("BizTalk.ESB.Extensions.PipelineComponentss.ImageDisassembler", Assembly.GetExecutingAssembly());
        private const string _IMAGEPROPERTIESNAMESPACE = @"https://BizTalk.ESB.Extensions.Schemas.ImageFileProperties";
        private string _MimeType;
        
        public string MimeType
        {
            get
            {
                return _MimeType;
            }
            set
            {
                _MimeType = value;
            }
        }
        
        #region IBaseComponent members
        /// <summary>
        /// Name of the component
        /// </summary>
        [Browsable(false)]
        public string Name
        {
            get
            {
                //return resourceManager.GetString("COMPONENTNAME", System.Globalization.CultureInfo.InvariantCulture);
                return NameProperty;
            }
        }
        
        /// <summary>
        /// Version of the component
        /// </summary>
        [Browsable(false)]
        public string Version
        {
            get
            {
                //return resourceManager.GetString("COMPONENTVERSION", System.Globalization.CultureInfo.InvariantCulture);
                return VersionProperty;
            }
        }
        
        /// <summary>
        /// Description of the component
        /// </summary>
        [Browsable(false)]
        public string Description
        {
            get
            {
                //return resourceManager.GetString("COMPONENTDESCRIPTION", System.Globalization.CultureInfo.InvariantCulture);
                return DescriptionProperty;
            }
        }
        #endregion
        
        #region IPersistPropertyBag members
        /// <summary>
        /// Gets class ID of component for usage from unmanaged code.
        /// </summary>
        /// <param name="classid">
        /// Class ID of the component
        /// </param>
        public void GetClassID(out System.Guid classid)
        {
            classid = new System.Guid("4B2855FB-3B55-4250-9637-788333C9AA56");
        }
        
        /// <summary>
        /// not implemented
        /// </summary>
        public void InitNew()
        {
        }
        
        /// <summary>
        /// Loads configuration properties for the component
        /// </summary>
        /// <param name="pb">Configuration property bag</param>
        /// <param name="errlog">Error status</param>
        public virtual void Load(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, int errlog)
        {
            object val = null;
            val = this.ReadPropertyBag(pb, "MimeType");
            if ((val != null))
            {
                this._MimeType = ((string)(val));
            }
        }
        
        /// <summary>
        /// Saves the current component configuration into the property bag
        /// </summary>
        /// <param name="pb">Configuration property bag</param>
        /// <param name="fClearDirty">not used</param>
        /// <param name="fSaveAllProperties">not used</param>
        public virtual void Save(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, bool fClearDirty, bool fSaveAllProperties)
        {
            this.WritePropertyBag(pb, "MimeType", this.MimeType);
        }
        
        #region utility functionality
        /// <summary>
        /// Reads property value from property bag
        /// </summary>
        /// <param name="pb">Property bag</param>
        /// <param name="propName">Name of property</param>
        /// <returns>Value of the property</returns>
        private object ReadPropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName)
        {
            object val = null;
            try
            {
                pb.Read(propName, out val, 0);
            }
            catch (System.ArgumentException )
            {
                return val;
            }
            catch (System.Exception e)
            {
                throw new System.ApplicationException(e.Message);
            }
            return val;
        }
        
        /// <summary>
        /// Writes property values into a property bag.
        /// </summary>
        /// <param name="pb">Property bag.</param>
        /// <param name="propName">Name of property.</param>
        /// <param name="val">Value of property.</param>
        private void WritePropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName, object val)
        {
            try
            {
                pb.Write(propName, ref val);
            }
            catch (System.Exception e)
            {
                throw new System.ApplicationException(e.Message);
            }
        }
        #endregion
        #endregion
        
        #region IComponentUI members
        /// <summary>
        /// Component icon to use in BizTalk Editor
        /// </summary>
        [Browsable(false)]
        public IntPtr Icon
        {
            get
            {
                //return ((System.Drawing.Bitmap)(this.resourceManager.GetObject("COMPONENTICON", System.Globalization.CultureInfo.InvariantCulture))).GetHicon();
                return new IntPtr();
            }
        }
        
        /// <summary>
        /// The Validate method is called by the BizTalk Editor during the build 
        /// of a BizTalk project.
        /// </summary>
        /// <param name="obj">An Object containing the configuration properties.</param>
        /// <returns>The IEnumerator enables the caller to enumerate through a collection of strings containing error messages. These error messages appear as compiler error messages. To report successful property validation, the method should return an empty enumerator.</returns>
        public System.Collections.IEnumerator Validate(object obj)
        {
            // example implementation:
            // ArrayList errorList = new ArrayList();
            // errorList.Add("This is a compiler error");
            // return errorList.GetEnumerator();
            return null;
        }
        #endregion
        
        /// <summary>
        /// this variable will contain any message generated by the Disassemble method
        /// </summary>
        private System.Collections.Queue _msgs = new System.Collections.Queue();
        
        #region IDisassemblerComponent members
        /// <summary>
        /// called by the messaging engine until returned null, after disassemble has been called
        /// </summary>
        /// <param name="pc">the pipeline context</param>
        /// <returns>an IBaseMessage instance representing the message created</returns>
        public Microsoft.BizTalk.Message.Interop.IBaseMessage GetNext(Microsoft.BizTalk.Component.Interop.IPipelineContext pc)
        {
            // get the next message from the Queue and return it
            Microsoft.BizTalk.Message.Interop.IBaseMessage msg = null;
            if ((_msgs.Count > 0))
            {
                msg = ((Microsoft.BizTalk.Message.Interop.IBaseMessage)(_msgs.Dequeue()));
            }
            return msg;
        }
        
        /// <summary>
        /// called by the messaging engine when a new message arrives
        /// </summary>
        /// <param name="pc">the pipeline context</param>
        /// <param name="inmsg">the actual message</param>
        public void Disassemble(Microsoft.BizTalk.Component.Interop.IPipelineContext pc, Microsoft.BizTalk.Message.Interop.IBaseMessage inmsg)
        {
            if (inmsg.BodyPart != null)
            {
                //Get the frame dimension list from the image stream 
                Image tiffImage = Image.FromStream(inmsg.BodyPart.GetOriginalDataStream());
                //get the globally unique identifier (GUID) 
                Guid objGuid = tiffImage.FrameDimensionsList[0];
                //create the frame dimension 
                FrameDimension dimension = new FrameDimension(objGuid);
                //Gets the total number of frames in the .tiff file 
                int noOfPages = tiffImage.GetFrameCount(dimension);

                ImageCodecInfo encodeInfo = null;
                ImageCodecInfo[] imageEncoders = ImageCodecInfo.GetImageEncoders();
                for (int j = 0; j < imageEncoders.Length; j++)
                {
                    if (imageEncoders[j].MimeType == this.MimeType)
                    {
                        encodeInfo = imageEncoders[j];
                        break;
                    }
                }

                foreach (Guid guid in tiffImage.FrameDimensionsList)
                {
                    FrameDimension currentFrame = new FrameDimension(guid);
                    for (int index = 0; index < noOfPages; index++)
                    {
                        tiffImage.SelectActiveFrame(currentFrame, index);
                        //tiffImage.Save(string.Concat(pstrOutputPath, @"\", OriginalFaxFileName.Substring(0, OriginalFaxFileName.Length - 4), index, ".TIF"), encodeInfo, null);
                        var newMessageStream = new VirtualStream();
                        tiffImage.Save(newMessageStream, encodeInfo, null);
                        var newMessage = this.CopyMessage(inmsg, pc, newMessageStream);
                        this.PromoteProperties(newMessage, noOfPages, objGuid, index);
                        _msgs.Enqueue(newMessage);
                    }
                }

            }
            
        }
        #endregion
        
        #region IProbeMessage members
        /// <summary>
        /// called by the messaging engine when a new message arrives
        /// checks if the incoming message is in a recognizable format
        /// if the message is in a recognizable format, only this component
        /// within this stage will be execute (FirstMatch equals true)
        /// </summary>
        /// <param name="pc">the pipeline context</param>
        /// <param name="inmsg">the actual message</param>
        public bool Probe(Microsoft.BizTalk.Component.Interop.IPipelineContext pc, Microsoft.BizTalk.Message.Interop.IBaseMessage inmsg)
        {
            // 
            // TODO: check whether you're interested in the given message
            // 
            return true;
        }
        #endregion

        #region Private members

        private void PromoteProperties(IBaseMessage message, int totalPages, Guid globalIdentifier, int currentPage)
        {
            message.Context.Promote("TotalPages", _IMAGEPROPERTIESNAMESPACE, totalPages);
            message.Context.Promote("GlobalIdentifier", _IMAGEPROPERTIESNAMESPACE, globalIdentifier.ToString());
            message.Context.Promote("PageNumber", _IMAGEPROPERTIESNAMESPACE, currentPage + 1);  // Account for zero indexing
        }

        private IBaseMessage GetMessageForQueue(IBaseMessage Msg, IPipelineContext pCtx)
        {
            IBaseMessage msg = Microsoft.Practices.ESB.Utilities.MessageHelper.CreateCloneMessage(pCtx, Msg);
            return msg;
        }

        //private IBaseMessage GetMessageForQueue(IBaseMessage Msg, IPipelineContext pCtx, IItinerary Itinerary)
        //{
        //    if (String.IsNullOrEmpty(this._LastMessage))
        //    {
        //        this._LastMessage = this.GetMessageContent(pCtx, Msg);
        //    }
        //    IBaseMessage msgCopy = this.CopyMessage(Msg, pCtx, this._LastMessage);

        //    EventLogger.Write("Message context count before writing itinerary: " + msgCopy.Context.CountProperties.ToString());
        //    if (this.ValidateItinerary)
        //    {
        //        Itinerary.Validate();
        //    }
        //    Itinerary.Initialize(msgCopy, Guid.NewGuid().ToString());
        //    Itinerary.Write(msgCopy);
        //    EventLogger.Write("Message context count after writing itinerary: " + msgCopy.Context.CountProperties.ToString());

        //    return msgCopy;
        //}

        //private IBaseMessage CopyMessage(IBaseMessage originalMessage, IPipelineContext pipelineContext, String messageContent)
        //{
        //    Byte[] msgData = Encoding.ASCII.GetBytes(messageContent);
        //    Stream outStream = new MemoryStream(msgData);
        //    IBaseMessage msgCopy = this.CopyMessage(originalMessage, pipelineContext, outStream);
        //    return msgCopy;
        //}

        private IBaseMessage CopyMessage(IBaseMessage originalMessage, IPipelineContext pipelineContext, Stream messageContent)
        {
            EventLogger.Write("Copying Message...");
            EventLogger.Write("Original Message Part Count: " + originalMessage.PartCount.ToString());

            // Message
            IBaseMessage msgCopy = pipelineContext.GetMessageFactory().CreateMessage();

            // Message Parts
            for (Int32 i = 0; i < originalMessage.PartCount; i++)
            {
                String partName = String.Empty;
                IBaseMessagePart part = originalMessage.GetPartByIndex(i, out partName);
                IBaseMessagePart partCopy = pipelineContext.GetMessageFactory().CreateMessagePart();
                EventLogger.Write("Copying message part " + partName);

                // Copy data stream
                messageContent.Seek(0, SeekOrigin.Begin);
                partCopy.Data = messageContent;
                pipelineContext.ResourceTracker.AddResource(partCopy.Data);

                // Copy properties
                partCopy.PartProperties = PipelineUtil.CopyPropertyBag(part.PartProperties, pipelineContext.GetMessageFactory());

                // Add part to message
                msgCopy.AddPart(partName, partCopy, (String.Compare(originalMessage.BodyPartName, partName, true, CultureInfo.CurrentCulture) == 0));
            }

            // Message Context
            msgCopy.Context = PipelineUtil.CloneMessageContext(originalMessage.Context);
            EventLogger.Write("Message Copy complete...");
            EventLogger.Write("Copied Message Part Count: " + msgCopy.PartCount.ToString());

            return msgCopy;
        }

        #endregion
    }
}
